#ifndef SIMPLEMATRIX_H
#define SIMPLEMATRIX_H

#include <QDebug>
#include <memory.h>
#include "FEM/CONTAINERS/abstractmatrix.h"

template <typename _DimType_>
class SimpleMatrix : public AbstractMatrix <_DimType_>
{
private:
    _DimType_ **_data = nullptr;
public:
    SimpleMatrix(const int nRows,const int nCols):
        AbstractMatrix<_DimType_>(nRows,nCols)
    {
        qDebug("SimpleMatrix: common constructor");
        _data = new _DimType_* [nRows];
        for(int i=0;i<this->nRows;++i)
        {
            _data[i] = new _DimType_ [nCols];
            memset(_data[i],0,this->nCols*sizeof(_DimType_));
        }
    }
    SimpleMatrix(const SimpleMatrix &m):
        AbstractMatrix<_DimType_>(m.nRows,m.nCols)
    {
        qDebug("SimpleMatrix: copy constructor");
        _data = new _DimType_* [this->nRows];
        for(int i=0;i<this->nRows;++i)
        {
            _data[i] = new _DimType_ [this->nCols];
            memcpy(_data[i],m._data[i],this->nCols*sizeof(_DimType_));
        }
    }
    SimpleMatrix& operator = (const SimpleMatrix &m)
    {
        qDebug("SimpleMatrix: copy (assigment) operator");
        if(_data!=nullptr)
        {
            for(int i=0;i<this->nRows;++i)
                delete [] _data[i];
            delete [] _data;
        }
        _data = new _DimType_* [this->nRows];
        for(int i=0;i<this->nRows;++i)
        {
            _data[i] = new _DimType_ [this->nCols];
            memcpy(_data[i],m._data[i],this->nCols*sizeof(_DimType_));
        }
        return *this;
    }

    SimpleMatrix& operator = (SimpleMatrix &&m)
    {
        qDebug("SimpleMatrix: copy rValue (assigment) operator");
        if(_data!=nullptr)
        {
            for(int i=0;i<this->nRows;++i)
                delete [] _data[i];
            delete [] _data;
        }
        _data = m._data;
        m._data = nullptr;
        return *this;
    }

    SimpleMatrix(SimpleMatrix &&m):
        AbstractMatrix<_DimType_>(m.nRows,m.nCols)
    {
        /// \todo
        qDebug("SimpleMatrix: rVlaue constructor");
        _data = m._data;
        m._data = nullptr;
    }


    _DimType_* operator [] (const int indexOfRow) const
    {
        return _data[indexOfRow];
    }
    _DimType_& operator () (const int indexOfRow, const int indexOfCol) const override
    {
        return _data[indexOfRow][indexOfCol];
    }
    friend SimpleMatrix operator + (const AbstractMatrix<_DimType_> &m1,
                                    const AbstractMatrix<_DimType_> &m2)
    {
        qDebug("SimpleMatrix: m1 + m2");
        SimpleMatrix _rez(m1.nRows, m1.nCols);
        for(int i=0;i<m1.nRows;++i)
            for(int j=0;j<m1.nCols;++j)
                 _rez(i,j) = m1(i,j) + m2(i,j);
        return _rez;
    }
    friend SimpleMatrix operator + (const AbstractMatrix<_DimType_> &m1,
                                    const _DimType_ &scalar)
    {
        qDebug("SimpleMatrix: m1 + sc");
        SimpleMatrix _rez(m1.nRows, m1.nCols);
        for(int i=0;i<m1.nRows;++i)
            for(int j=0;j<m1.nCols;++j)
                 _rez(i,j) = m1(i,j) + scalar;
        return _rez;
    }
    friend SimpleMatrix operator + (const _DimType_ &scalar,
                                    const AbstractMatrix<_DimType_> &m2)
    {
        qDebug("SimpleMatrix: sc + m2");
        SimpleMatrix _rez(m2.nRows, m2.nCols);
        for(int i=0;i<m2.nRows;++i)
            for(int j=0;j<m2.nCols;++j)
                 _rez(i,j) = scalar + m2(i,j);
        return _rez;
    }
    friend SimpleMatrix operator - (const AbstractMatrix<_DimType_> &m1,
                                    const AbstractMatrix<_DimType_> &m2)
    {
        qDebug("SimpleMatrix: m1 - m2");
        SimpleMatrix _rez(m1.nRows, m1.nCols);
        for(int i=0;i<m1.nRows;++i)
            for(int j=0;j<m1.nCols;++j)
                 _rez(i,j) = m1(i,j) - m2(i,j);
        return _rez;
    }
    friend SimpleMatrix operator - (const AbstractMatrix<_DimType_> &m1,
                                    const _DimType_ &scalar)
    {
        qDebug("SimpleMatrix: m1 - sc");
        SimpleMatrix _rez(m1.nRows, m1.nCols);
        for(int i=0;i<m1.nRows;++i)
            for(int j=0;j<m1.nCols;++j)
                 _rez(i,j) = m1(i,j) - scalar;
        return _rez;
    }
    friend SimpleMatrix operator - (const _DimType_ &scalar,
                                    const AbstractMatrix<_DimType_> &m2)
    {
        qDebug("SimpleMatrix: sc - m2");
        SimpleMatrix _rez(m2.nRows, m2.nCols);
        for(int i=0;i<m2.nRows;++i)
            for(int j=0;j<m2.nCols;++j)
                 _rez(i,j) = scalar + m2(i,j);
        return _rez;
    }
    friend SimpleMatrix operator * (const AbstractMatrix<_DimType_> &m1,
                                    const AbstractMatrix<_DimType_> &m2)
    {
        qDebug("SimpleMatrix: m1 * m2");
        SimpleMatrix _rez(m1.nRows, m2.nCols);
        for(int i=0;i<m1.nRows;++i)
            for(int j=0;j<m2.nCols;++j)
            {
                _DimType_ _sum = 0;
                for(int k=0;k<m1.nCols;++k)
                    _sum += m1(i,k) * m2(k,j);
                _rez(i,j) = _sum;
            }
        return _rez;
    }
    friend SimpleMatrix operator * (const AbstractMatrix<_DimType_> &m1,
                                    const _DimType_ &scalar)
    {
        qDebug("SimpleMatrix: m1 * sc");
        SimpleMatrix _rez(m1.nRows, m1.nCols);
        for(int i=0;i<m1.nRows;++i)
            for(int j=0;j<m1.nCols;++j)
                 _rez(i,j) = m1(i,j) * scalar;
        return _rez;
    }
    friend SimpleMatrix operator * (const _DimType_ &scalar,
                                    const AbstractMatrix<_DimType_> &m2)
    {
        qDebug("SimpleMatrix: sc * m2");
        SimpleMatrix _rez(m2.nRows, m2.nCols);
        for(int i=0;i<m2.nRows;++i)
            for(int j=0;j<m2.nCols;++j)
                 _rez(i,j) = scalar * m2(i,j);
        return _rez;
    }
    void transpose() override
    {
        this->isTransposed = !this->isTransposed;
        for(int i=0;i<this->nRows;++i)
            for(int j=1+i;j<this->nCols;++j)
            {
                _DimType_ _tmpVal = _data[i][j];
                _data[i][j] = _data[j][i];
                _data[j][i] = _tmpVal;
            }
    }

    ~SimpleMatrix() override
    {
        if(_data!=nullptr)
        {
            qDebug("SimpleMatrix: destructor");
            for(int i=0;i<this->nRows;++i)
                delete [] _data[i];
            delete [] _data;
        }
        else qDebug("SimpleMatrix: destructor : _data is <nullptr>");
    }
};
#endif // SIMPLEMATRIX_H
